package diandiansong.library.http;

import android.annotation.SuppressLint;

import diandiansong.library.http.okhttputils.OkHttpUtils;
import diandiansong.library.http.okhttputils.callback.StringCallback;
import diandiansong.library.http.okhttputils.https.HttpsUtils;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import diandiansong.library.utils.JsonParse;
import diandiansong.library.utils.Logger;

/**
 * Created by act on 2016/7/15.
 * 与Server交互http get/post
 */
public abstract class Task {

    protected static Logger logger = Logger.getLogger();

    @SuppressLint("SimpleDateFormat")
    private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    protected abstract String getAppKey();

    protected abstract String getAppSecrt();

    protected static <T> void get(String url, Map<String, String> parmas, final Class<T> clazz, final CCallBack<T> cCallBack) {
        //证书配置
        HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
                //其他配置
                .build();
        OkHttpUtils.initClient(okHttpClient);

        OkHttpUtils.get().url(url)
                .params(parmas)
                .build().execute(new StringCallback() {
            @Override
            public void onError(String response, Call call, Exception e, int id) {
                logger.e("onError() called with: " + "call = [" + call + "], e = [" + e + "]");
                cCallBack.onFailure(ErrorCode.ERROR_NET, "");
            }

            @Override
            public void onResponse(String response, int id) {
                logger.d("onResponse() called with: " + "response = [" + response + "]");
                T entity = JsonParse.parse(response, clazz);
                if (entity != null) {
                    cCallBack.onFinish(entity);

                } else {
                    logger.e("onError() called with: " + "parse = [error]");
                    //cCallBack.onFailure(ErrorCode.ERROR_PARSE, "");
                }
            }
        });
    }

    protected static <T> void post(String url, Map<String, String> parmas, final Class<T> clazz, final CCallBack<T> cCallBack) {
        //证书配置
        HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
                //其他配置
                .build();
        OkHttpUtils.initClient(okHttpClient);

        OkHttpUtils.post().url(url)
                .params(parmas)
                .build().execute(new StringCallback() {
            @Override
            public void onBefore(Request request, int id) {
                super.onBefore(request, id);
                logger.d("onBefore() called with: " + "request = [" + request + "]");
            }


            @Override
            public void onError(String response, Call call, Exception e, int id) {
                logger.e("onError() called with: " + "call = [" + call + "], e = [" + e + "]");
                cCallBack.onFailure(ErrorCode.ERROR_NET, "");
            }

            @Override
            public void onResponse(String response, int id) {
                logger.d("onResponse() called with: " + "response = [" + response + "]");
                T entity = JsonParse.parse(response, clazz);
                if (entity != null) {
                    cCallBack.onFinish(entity);

                } else {
                    logger.e("onError() called with: " + "parse = [error]");
                    cCallBack.onFailure(ErrorCode.ERROR_PARSE, "");
                }
            }
        });
    }

    protected static <T> void get(String url, Map<String, String> parmas, final CCallBack<T> cCallBack) {
        //证书配置
        HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
                //其他配置
                .build();
        OkHttpUtils.initClient(okHttpClient);

        OkHttpUtils.get().url(url)
                .params(parmas)
                .build().execute(new StringCallback() {


            @Override
            public void onError(String response, Call call, Exception e, int id) {
                logger.e("onError() called with: " + "call = [" + call + "], e = [" + e + "]");
                cCallBack.onFailure(ErrorCode.ERROR_NET, "");
            }

            @Override
            public void onResponse(String response, int id) {
                logger.d("onResponse() called with: " + "response = [" + response + "]");
                Type type = ((ParameterizedType) cCallBack.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
                T entity = JsonParse.parseObjFromType(response, type);
                if (entity != null) {
                    cCallBack.onFinish(entity);

                } else {
                    logger.e("onError() called with: " + "parse = [error]");
                    cCallBack.onFailure(ErrorCode.ERROR_PARSE, "");
                }
            }
        });
    }

    protected static <T> void post(String url, Map<String, String> parmas, final CCallBack<T> cCallBack) {
        //证书配置
        HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
                //其他配置
                .build();
        OkHttpUtils.initClient(okHttpClient);

        OkHttpUtils.post().url(url)
                .params(parmas)
                .build().execute(new StringCallback() {
            @Override
            public void onBefore(Request request, int id) {
                super.onBefore(request, id);
                logger.d("onBefore() called with: " + "request = [" + request + "]");
            }

            @Override
            public void onError(String response, Call call, Exception e, int id) {
                logger.e("onError() called with: " + "call = [" + call + "], e = [" + e + "]");
                cCallBack.onFailure(ErrorCode.ERROR_NET, "");
            }

            @Override
            public void onResponse(String response, int id) {
                logger.d("onResponse() called with: " + "response = [" + response + "]");
                Type type = ((ParameterizedType) cCallBack.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
                T entity = JsonParse.parseObjFromType(response, type);
                if (entity != null) {
                    cCallBack.onFinish(entity);

                } else {
                    logger.e("onError() called with: " + "parse = [error]");
                    cCallBack.onFailure(ErrorCode.ERROR_PARSE, "");
                }
            }
        });
    }

    ///////////////////////////////////////////////////////////////////////////
    // 参数签名
    ///////////////////////////////////////////////////////////////////////////

    /**
     * 根据必要参数拼接上 app_key timestamp sign 等参数
     *
     * @param params 参数
     * @return params map
     */
    protected static Map<String, String> getParamsMap(Task task, String... params) {
        if (params.length % 2 != 0) {
            throw new IllegalArgumentException("params must with key and value");
        }
        Map<String, String> paramsMap = new HashMap<>();
        for (int i = 0; i < params.length; i = i + 2) {
            paramsMap.put(params[i], params[i + 1]);
        }
        paramsMap.put("app_key", task.getAppKey());
        paramsMap.put("timestamp", DATE_FORMAT.format(new Date()));

        try {
            String sign = createSign(paramsMap, task.getAppSecrt());
            paramsMap.put("sign", sign);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return paramsMap;
    }

    /**
     * 根据参数和secret生成签名
     */
    private static String createSign(Map<String, String> params, String secret) throws IOException {
        // 第一步：检查参数是否已经排序
        String[] keys = params.keySet().toArray(new String[0]);
        Arrays.sort(keys);

        // 第二步：把所有参数名和参数值串在一起
        StringBuilder query = new StringBuilder();
        query.append(secret);
        for (String key : keys) {
            String value = params.get(key);
            if (!"sign".equals(key)) {
                query.append(key).append(value);
            }
        }

        // 第三步：使用MD5加密
        byte[] bytes;
        query.append(secret);
        bytes = encryptMD5(query.toString());

        // 第四步：把二进制转化为大写的十六进制
        return byte2hex(bytes);
    }

    private static final String CHARSET_UTF8 = "utf-8";

    /**
     * 对字符串采用UTF-8编码后，用MD5进行摘要。
     */
    private static byte[] encryptMD5(String data) throws IOException {
        byte[] bytes = data.getBytes(CHARSET_UTF8);
        return encryptMD5(bytes);
    }

    /**
     * 对字节流进行MD5摘要。
     */
    private static byte[] encryptMD5(byte[] data) throws IOException {
        byte[] bytes;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            bytes = md.digest(data);
        } catch (GeneralSecurityException gse) {
            throw new IOException(gse.toString());
        }
        return bytes;
    }

    /**
     * 把字节流转换为十六进制表示方式。
     */
    private static String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (byte aByte : bytes) {
            String hex = Integer.toHexString(aByte & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }


}
